<?php
/**
 * websocket 握手事件，同时进行用户认证操作
 */
namespace app\Listener;

use Firebase\JWT\JWT;
use Swoole\Http\Request;
use Swoole\Http\Response;
use Swostar\Event\Listener;
use Swostar\Server\WebSocket\WebSocketServer;

class WsHandShakeListener extends Listener
{
    protected $name = 'ws.handShake';

    public function run(WebSocketServer $server = null, Request $request = null, Response $response = null)
    {
        // // 接收websocket传入参数
        // $token = $request->header['sec-websocket-protocol'];

        // // 进行用户认证
        // if (empty($token) || !$this->checkAuth($server, $token, $request->fd)) {
        //     $response->end('认证失败');
        //     return false;
        // }

        // echo '认证成功' . PHP_EOL;

        // websocket自带必备
        $this->handShake($request, $response);
    }

    protected function checkAuth(WebSocketServer $server, $token, $fd)
    {
        try {
            $config = app('config');
            // 获取公钥
            $key = $config->get('server.routeServer.jwt.key');

            // jwt验证
            $jwt = JWT::decode($token, $key, $config->get('server.routeServer.jwt.alg'));

            // 用户基本信息
            $userInfo = $jwt->data;

            // 用户连接的imserver
            $url = $userInfo->serverUrl;

            // 获取redis对象，将用户信息存入redis
            $server->getRedis()->hSet($key, $userInfo->uid, json_encode([
                'fd' => $fd,
                'name' => $userInfo->name,
                'serverUrl' => $url
            ]));

            return true;
        } catch (\Throwable $th) {
            return false;
        }
    }

    /**
     * 原有方法
     * @param mixed $request 
     * @param mixed $response 
     * @return false|void 
     */
    protected function handshake($request, $response)
    {
        // websocket握手连接算法验证
        $secWebSocketKey = $request->header['sec-websocket-key'];
        $patten = '#^[+/0-9A-Za-z]{21}[AQgw]==$#';
        if (0 === preg_match($patten, $secWebSocketKey) || 16 !== strlen(base64_decode($secWebSocketKey))) {
            $response->end();
            return false;
        }
        
        $key = base64_encode(
            sha1(
                $request->header['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
                true
            )
        );

        $headers = [
            'Upgrade' => 'websocket',
            'Connection' => 'Upgrade',
            'Sec-WebSocket-Accept' => $key,
            'Sec-WebSocket-Version' => '13',
        ];

        // WebSocket connection to 'ws://127.0.0.1:9502/'
        // failed: Error during WebSocket handshake:
        // Response must not include 'Sec-WebSocket-Protocol' header if not present in request: websocket
        if (isset($request->header['sec-websocket-protocol'])) {
            $headers['Sec-WebSocket-Protocol'] = $request->header['sec-websocket-protocol'];
        }

        foreach ($headers as $key => $val) {
            $response->header($key, $val);
        }

        $response->status(101);
        $response->end();
    }
}
